// DOMTree.cpp : implementation file
#include "stdafx.h"
#include "DOMTree.h"
#include "DOMNode.h"
#include <queue>

#define IDM_EXPANDALL 10001
#define IDM_COLLAPSEALL 10002

IMPLEMENT_DYNAMIC(CDOMTree, CTreeCtrl)

BEGIN_MESSAGE_MAP(CDOMTree, CTreeCtrl)
	ON_NOTIFY_REFLECT(TVN_DELETEITEM, OnTvnDeleteitem)
	ON_WM_DESTROY()
	ON_NOTIFY_REFLECT(TVN_SELCHANGED, OnTvnSelchanged)
	ON_COMMAND(IDM_EXPANDALL,OnExpandAll)
	ON_COMMAND(IDM_COLLAPSEALL,OnCollapseAll)
	ON_WM_RBUTTONDOWN()
END_MESSAGE_MAP()

void CDOMTree::OnTvnDeleteitem(NMHDR *pNMHDR, LRESULT *pResult)
{/*
	LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
	TVITEM* pItem = &pNMTreeView->itemOld;

	if ( pItem && pItem->lParam)
	{
		IUnknown* pNodeUnknown = reinterpret_cast<IUnknown*>(pItem->lParam);

#if defined(_DEBUG)
		long relcnt = pNodeUnknown->Release(); // for AddRef when added to the tree item data
		ATLASSERT( relcnt == 0);
#else
		pNodeUnknown->Release(); 
#endif
	}
	*/
	*pResult = 0;
}
void CDOMTree::OnDestroy()
{
	CTreeCtrl::OnDestroy();
}
void CDOMTree::UpdateDOM(IHTMLDocument2* pDoc)
{
	SetRedraw( FALSE);
	DeleteAllItems();
	m_spDoc2 = pDoc;
	PrepareDOMTree();
	SetRedraw( TRUE);
}

//a breadth first tree creating algorithm
bool CDOMTree::PrepareDOMTree()
{
	ASSERT( m_MyDOMTree.Update(m_spDoc2) );
	m_MyDOMTree.BuildTree();
	
	std::queue<HTREEITEM> queue_tree_nodes;
	std::queue<DOMNode* > queue_DOMNodes;

	queue_DOMNodes.push(m_MyDOMTree.GetRoot());
	queue_tree_nodes.push(TVI_ROOT);

	while( !queue_tree_nodes.empty() )
	{
		DOMNode* pDOMNode=queue_DOMNodes.front();
		HTREEITEM parent=queue_tree_nodes.front();
		queue_DOMNodes.pop();
		queue_tree_nodes.pop();

		HTREEITEM thisnode=InsertDOMNode(pDOMNode,parent);


		for(std::vector<DOMNode*>::iterator i=pDOMNode->m_Children.begin();i!=pDOMNode->m_Children.end();i++){
			queue_DOMNodes.push(*i);
			queue_tree_nodes.push(thisnode);			
		}
	}

	return true;
}

HTREEITEM CDOMTree::InsertDOMNode( DOMNode* pNode, HTREEITEM hparent)
{
	CString strNodeName;
	
	UINT nMask=  TVIF_TEXT | TVIF_PARAM;
	LPARAM lParam;
	HTREEITEM hParent= hparent;
	HTREEITEM hInsertAfter = TVI_LAST;

	strNodeName=pNode->GetTagName();
		
	lParam = reinterpret_cast<LPARAM>(pNode);
	HTREEITEM hthisItem = InsertItem(
		nMask,
		strNodeName,
		0,
		0,
		0,
		0,
		lParam,
		hparent,
		hInsertAfter
		);
	return hthisItem;
}

void CDOMTree::OnTvnSelchanged(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
	TVITEM* pItem = &pNMTreeView->itemNew;
	DOMNode* pDOMNode=reinterpret_cast<DOMNode*>(pItem->lParam);	
	DOMNode* pDOMNodeOld=reinterpret_cast<DOMNode*>(pNMTreeView->itemOld.lParam);	

	if(!pDOMNode)
		return;
	if(!pDOMNodeOld)
		return;
	pDOMNodeOld->RestoreRect();
	pDOMNode->ShowRect();
	CString strMsg=pDOMNode->toString();
	LPARAM lParam=reinterpret_cast<LPARAM>(&strMsg);
	::SendMessage(this->GetParent()->GetSafeHwnd(),WM_DOMNODE_INFO,0,lParam);
	
	*pResult = 0;
}
void CDOMTree:: OnExpandAll()
{
	ExpandAll();
}
void CDOMTree::OnCollapseAll()
{	
	CollapseAll();
}
void CDOMTree::OnRButtonDown(UINT nFlags, CPoint point)
{
	CMenu context;
	context.CreatePopupMenu();
	ClientToScreen(&point);
	context.AppendMenu(MF_STRING|MF_ENABLED,IDM_EXPANDALL,_T("Expand All"));
	context.AppendMenu(MF_STRING|MF_ENABLED,IDM_COLLAPSEALL,_T("Collapse All"));
	context.TrackPopupMenu( TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON, point.x, point.y, this);
	CTreeCtrl::OnRButtonDown(nFlags, point);
}
void CDOMTree::ExpandAll()
{
	HTREEITEM hCurrent = GetSelectedItem();
	if ( !hCurrent)
		hCurrent = GetRootItem();
	if ( hCurrent)
	{
		SetRedraw( FALSE);
		// When expanding we must expand parent before its children
		unsigned int flags = DOPARENTFIRST;
		DoForItemAndChildren( hCurrent, &CDOMTree::ExpandItem, flags);
		EnsureVisible( hCurrent);
		SetRedraw( TRUE);
	}
}
// Recursive helper that calls a method, that param fp points to, for a hParent node
// and all of its children, its children's children, etc.
// This is one of few cases where I used ->* notation for method call through a pointer
void CDOMTree::DoForItemAndChildren( HTREEITEM hParent, FOREACHITEM fp, unsigned int flags /*= 0*/)
{
	// DOPARENTFIRST flag tells us if we want fp to be called on parent node first, then children, or viceversa
	if ( flags & DOPARENTFIRST)
	{
		if ( !( flags & SKIPTOPPARENT))
			(this->*fp)( hParent);
	}

	HTREEITEM hChild = GetChildItem(hParent);
	while ( hChild)
	{
		DoForItemAndChildren( hChild, fp, ( flags & DOPARENTFIRST) ? DOPARENTFIRST : 0);
		hChild = GetNextSiblingItem( hChild);
	};
	if ( !(flags & DOPARENTFIRST))
	{
		if ( !( flags & SKIPTOPPARENT))
			(this->*fp)( hParent);
	}
}
void CDOMTree::CollapseAll()
{
	HTREEITEM hCurrent = GetSelectedItem();
	if ( !hCurrent)
		hCurrent = GetRootItem();
	if ( hCurrent)
	{
		SetRedraw( FALSE);
		Expand( hCurrent, TVE_COLLAPSE);
		SetRedraw( TRUE);
	}
}